<?php


namespace App\Services;

use App\Exceptions\ApiException;
use App\Models\ActionLog;
use App\Models\BookCatalog;
use App\Models\BookCatalogPdf;
use App\Models\BookTitle;
use App\Models\BookTitleMerge;
use App\Models\CompanyUser;
use App\Models\DocArchive;
use App\Models\PartCompany;
use App\Models\PartCompanyClassify;
use App\Models\PhotoClassify;
use App\Models\Project;
use App\Models\ProjectClassify;
use App\Models\RefreshLog;
use App\Models\Role;
use App\Models\UserLoginInfo;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;

class CommonService
{
    /**
     * 获取一个
     *
     * @param int $type
     * @return string
     * @author: lxk
     * @date: 2025-07-03 16:33
     */
    public static function getBaseDirPathByProClassId() : string
    {
        $partCompanyId     = request()->input('part_company_id');
        $projectClassifyId = request()->input('project_classify_id');
        $proClassName      = ProjectClassify::query()->where('id', $projectClassifyId)->value('title');
        $partCompany       = PartCompany::query()->where('id', $partCompanyId)->first();
        $projectCode       = Project::query()->where('id', $partCompany->project_id)->value('document_number');
        $baseDirPath       = "excels/【{$projectCode}】{$partCompany->title}_{$proClassName}_{$typeName}";

        CommonService::createDir($baseDirPath); //创建文件夹

        return $baseDirPath;
    }

    /**
     * 通过案卷序号得到bookTitleIds
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $range
     * @return array
     * @throws ApiException
     * @author: lxk
     * @date: 2025-01-14 13:17
     */
    public static function getIdsBySerialRange($partCompanyId, $projectClassifyId, $range) : array
    {
        if (empty($range)) return [];
        $needIds = [];
        foreach ($range as $val) {
            $ids = self::getBookTitleInstance($partCompanyId, $projectClassifyId)
                ->whereBetween('serial_no', [$val['start'], $val['end']])
                ->pluck('id');
            if ($ids->isNotEmpty()) {
                $ids = $ids->toArray();
                foreach ($ids as $id) {
                    if (!in_array($id, $needIds)) {
                        $needIds[] = $id;
                    }
                }
            }
        }
        sort($needIds);

        return $needIds;
    }

    /**
     * 使用递归获取结果
     *
     * @param array $data
     * @param array $result
     * @return array
     * @author: lxk
     * @date: 2025-01-08 14:05
     */
    public static function getRecursionData(array $data, array &$result = []) : array
    {
        foreach ($data as $val) {
            $result[] = $val['id'];
            if (isset($val['children']) && !empty($val['children'])) {
                self::getRecursionData($val['children'], $result);
            }
        }
        return $result;
    }

    /**
     * 删除指定前缀的 redis 数据
     */
    public static function delSpecificRedisKey($pattern)
    {
        $iterator = null; // 初始迭代器设置为null
        $pattern  = $pattern.'*'; // 指定匹配模式
        $count    = 100; // 每次迭代返回的键的最大数量
        while ($keys = redis()->scan($iterator, $pattern, $count)) { // 使用SCAN命令迭代匹配的键
            foreach ($keys as $key) {
                redis()->del($key); // 删除匹配到的键
            }
        }
    }

    /**
     * 获取参建单位案卷、卷内等数量统计最新更新时间
     *
     * @param $projectId
     * @param $partCompanyId
     * @return \Illuminate\Database\Eloquent\HigherOrderBuilderProxy|mixed|string
     * @author: lxk
     * @date: 2024-10-19 16:48
     */
    public static function getFreshTime($projectId)
    {
        $info = RefreshLog::query()
            ->where('project_id', $projectId)
            ->orderByDesc('id')
            ->first();

        return $info ? Carbon::parse($info->pre_refresh_time)->diffForHumans()  : '暂无更新';
    }

    /**
     * 获取【案卷】数据表实例
     */
    public static function getDocArchiveInstance($companyId = 0)
    {
        if (!$companyId) {
            $companyId = company_id();
        }
        $docArchiveTable = "doc_archives_{$companyId}";
        if (!Schema::hasTable($docArchiveTable)) {
            DB::update("create table if not exists {$docArchiveTable}   like doc_archives");
        }
        $docArchive = new DocArchive();

        return $docArchive->setTable($docArchiveTable);
    }

    /**
     * 判断文件合法性
     */
    public static function isFileValid($request)
    {
        if (!$request->hasFile('file')) {
            throw new ApiException('请导入文件');
        }
        $file = $request->file('file'); //获取上传的文件
        if (!in_array($file->getClientOriginalExtension(), ['xls', 'xlsx'])) {
            throw new ApiException('文件后缀必须为 .xls');
        }

        return true;
    }

    /**
     * 获取数据表，分表
     *
     * @param $baseTable
     * @return array
     * @throws ApiException
     * @throws \Doctrine\DBAL\Exception
     * @author: lxk
     * @date: 2024-10-26 15:01
     */
    public static function getSubTables($baseTable) : array
    {
        $tables    = [];
        $baseTable = $baseTable . '_';
        $allTables = DB::connection()->getDoctrineSchemaManager()->listTableNames(); //获取全部数据表名
        foreach ($allTables as $table) {
            if (str_contains($table, $baseTable) && !str_contains($table, 'null') && !str_contains($table, 'undefined')) {
                $tableParam = CommonService::getTableParams($table);
                if ($tableParam['id2'] ) {
                    CommonService::getBookCatalogInstance($tableParam['id1'], $tableParam['id2']); //防止案卷有数据，但是卷内为空，表删除，导致的关联查询报错
                    $tables[] = $table;
                }
            }
            if (str_contains($table, 'null') || str_contains($table, 'null')) Schema::dropIfExists($table);
        }
        return $tables;
    }

    /**
     * 获取案卷或者卷内分表的 $partCompanyId 和 $projectClassifyId
     * 如分表： book_titles_28_77
     * @author: lxk
     * @date: 2023/5/29 17:41
     */
    public static function getTableParams($table)
    {
        $pos = strrpos($table, '_');
        $id2 = substr($table, $pos + 1);

        $subTable = substr($table, 0, $pos);
        $pos2     = strrpos($subTable, '_');
        $id1      = substr($subTable, $pos2 + 1);

        return [
            'id1' => $id1, //参建单位id
            'id2' => $id2, //项目分类id
        ];
    }

    /**
     * 判断是不是正式服，只有 tielu.qichenglantai.com 下删除案卷，卷内等才会同步删除PDF
     */
    public static function isNeedUpdatePaperCount($partCompanyId, $status)
    {
        PartCompany::query()->where('id', $partCompanyId)->update(['need_update_statistic' => $status]);
    }


    /**
     * 判断是不是正式服，只有 tielu.qichenglantai.com 下删除案卷，卷内等才会同步删除PDF
     */
    public static function isDelOssObject()
    {
        return (request()->getHost() == 'tielu.qichenglantai.com');
    }

    /**
     * 检查页号格式是否合法。应为纯数字，或者 15-30 | 15~30 这种格式
     *
     * @param $pageNum
     * @return false|int|int[]|string|string[]
     * @author: lxk
     * @date: 2024-10-23 10:55
     */
    public static function checkPageNum($pageNum)
    {
        if (empty($pageNum)) return false;

        if (!is_numeric($pageNum)) {
            $pageNum = format_str($pageNum);
            if (!$pos = strpos($pageNum, '～')) {
                return false;
            } else {
                $smallPage = mb_substr($pageNum, 0, $pos);
                $result    = json_encode(mb_substr($pageNum, $pos + 1));
                if (!empty($result)) {
                    $bigPage = mb_substr($pageNum, $pos + 1);
                } else {
                    $bigPage = mb_substr($pageNum, $pos + 3);
                }
                if (!is_numeric($smallPage) || !is_numeric($bigPage)) {
                    return false;
                }
                if ($smallPage > $bigPage) {
                    return false;
                }
                return [
                    'small_page' => $smallPage,
                    'big_page'   => $bigPage
                ];
            }
        }
        return $pageNum;
    }

    /**
     * 获取卷内数据的页码，如果不是 number 类型，只获取开头数组 返回数字
     *
     * @param $pageMum
     * @return int
     * @author: lxk
     * @date: 2024-10-22 16:55
     */
    public static function getPageInt($pageMum) : int
    {
        if (!is_numeric($pageMum)) {
            $pageNum = format_str($pageMum);
            $pos     = strpos($pageNum, '～');
            $pageMum = mb_substr($pageNum, 0, $pos);
        }
        return (int)$pageMum;
    }


    public static function getChildSerialNos($obj, $ids)
    {
        $serialNos = $obj->whereIn('id', $ids)->orderBy('serial_no')->pluck('serial_no')->toArray();

        return '【' . min($serialNos) . '~' . max($serialNos) . '】';
    }

    /**
     * 获取一个项目下的全部有效参见单位和项目分类
     *
     * @param $projectId
     * @param int $partCompanyId
     * @return array
     * @author: lxk
     * @date: 2024-12-03 14:17
     */
    public static function getIdsByProjectId($projectId, int $partCompanyId = 0) : array
    {
        if ($partCompanyId) {
            $partCompanyIds = [$partCompanyId];
        } else {
            $partCompanyIds = PartCompany::query()->where('project_id', $projectId)->pluck('id')->toArray();
        }
        $projectClassifyIds = PartCompanyClassify::query()
            ->distinct()
            ->whereIn('part_company_id', $partCompanyIds)
            ->orderBy('project_classify_id')
            ->pluck('project_classify_id')
            ->toArray();

        return [
            'part_company_ids'     => $partCompanyIds,
            'project_classify_ids' => $projectClassifyIds,
        ];
    }

    /**
     * 获取全部有效的参见单位和项目分类
     *
     * @return array
     * @author: lxk
     * @date: 2024-10-19 18:00
     */
    public static function getPartAndProjectClassifyIds() : array
    {
        $partCompanyIds = PartCompanyClassify::query()
            ->distinct()
            ->orderBy('part_company_id')
            ->pluck('part_company_id')
            ->toArray();

        $projectClassifyIds = PartCompanyClassify::query()->distinct()
            ->orderBy('project_classify_id')
            ->pluck('project_classify_id')
            ->toArray();

        return [
            'part_company_ids'     => $partCompanyIds,
            'project_classify_ids' => $projectClassifyIds,
        ];
    }

    /**
     * 获取档案号的两部分
     * J•SJKZ•5ZH123/169-014 分为 J•SJKZ•5ZH123/169 和 014
     */
    public static function getArchivesPart($archivesNo)
    {
        $pos = strrpos($archivesNo, '-');
        return [
            'main' => substr($archivesNo, 0, $pos), //获取主档案号
            'ext'  => substr($archivesNo, $pos + 1) //获取序号
        ];
    }

    /**
     * 获取一个案卷查询实例
     * 写这个的原因是因为【案卷合并】带来的影响
     * 要求，合并后的整体数据，只有超管和管理员能看到，其他用户只能看到自己负责的那部分
     * @author: lxk
     * @date: 2023/12/23 13:18
     */
    public static function getBookTitleSearchInstance($partCompanyId, $projectClassifyId)
    {
        return self::getBookTitleInstance($partCompanyId, $projectClassifyId);
//        if (is_company_admin() || is_super_admin()) {
//            return $bookTitleInstance->where('project_classify_id', $projectClassifyId);
//        } else {
//            return $bookTitleInstance->where('part_company_id', $partCompanyId)->where('project_classify_id', $projectClassifyId);
//        }
    }

    /**
     * 合并后的案卷无法继续操作
     * 导入，新增，删除，编辑
     */
    public static function isHadMerge($partCompanyId, $projectClassifyId)
    {
        return BookTitleMerge::query()->where('project_classify_id', $projectClassifyId)->value('merge_count');
    }

    /**
     * 获取【案卷】数据表实例
     */
    public static function getBookTitleInstance($partCompanyId, $projectClassifyId)
    {
        $bookTitleTable = "book_titles_{$partCompanyId}_{$projectClassifyId}";
        if (empty($partCompanyId) || empty($projectClassifyId)) {
            throw new ApiException('缺少必要参数！');
        }
        if (!Schema::hasTable($bookTitleTable)) {
            DB::update("create table if not exists {$bookTitleTable}   like book_titles");
        }
        $bookTitle = new BookTitle();

        return $bookTitle->setTable($bookTitleTable);
    }

    /**
     * 获取【卷内】表实例
     */
    public static function getBookCatalogInstance($partCompanyId, $projectClassifyId)
    {
        $bookCatalogTable = "book_catalogs_{$partCompanyId}_{$projectClassifyId}";
        if (empty($partCompanyId) || empty($projectClassifyId)) {
            throw new ApiException('缺少必要参数！');
        }
        if (!Schema::hasTable($bookCatalogTable)) {
            DB::update("create table if not exists {$bookCatalogTable}   like book_catalogs");
        }
        $bookCatalog = new BookCatalog();

        return $bookCatalog->setTable($bookCatalogTable);
    }

    /**
     * 获取【卷内PDF】表实例
     */
    public static function getBookCatalogPdfInstance($partCompanyId, $projectClassifyId)
    {
        $bookCatalogPdfTable = "book_catalog_pdfs_{$partCompanyId}_{$projectClassifyId}";
        if (empty($partCompanyId) || empty($projectClassifyId)) {
            throw new ApiException('缺少必要参数！');
        }
        if (!Schema::hasTable($bookCatalogPdfTable)) {
            DB::update("create table if not exists {$bookCatalogPdfTable}   like book_catalog_pdfs");
        }
        $bookCatalogPdf = new BookCatalogPdf();

        return $bookCatalogPdf->setTable($bookCatalogPdfTable);
    }

    /**
     * 获取 项目名、参建单位名、项目分类名
     */
    public static function getProjectContent($partCompanyId, $classifyId, $type = 1)
    {
        $partCompany = PartCompany::query()->find($partCompanyId);
        $projectName = Project::query()->where('id', $partCompany->project_id)->value('title');
        if ($type == 2) {
            $classifyName = PhotoClassify::query()->where('id', $classifyId)->value('title'); //照片分类
        } else {
            $classifyName = ProjectClassify::query()->where('id', $classifyId)->value('title'); //项目分类
        }
        return [
            'project'      => $projectName,
            'part_company' => $partCompany->title,
            'classify'     => $classifyName,
        ];
    }

    /**
     * 添加【科技档案】 项目操作日志
     */
    public static function addProjectActionLog($event, $objectId, $partCompanyId, $classifyId = 0)
    {
        $content = self::getProjectContent($partCompanyId, $classifyId);
        $detail  = "{$content['project']}/{$content['part_company']}/{$content['classify']}【分表后缀:{$partCompanyId}_{$classifyId}; 数据ID:{$objectId}】";

        return self::addActionLog($event, $detail);
    }

    /**
     * 添加【照片档案】操作日志
     */
    public static function addPhotoArchivesLog($event, $objectId, $partCompanyId, $classifyId = 0)
    {
        $content = self::getProjectContent($partCompanyId, $classifyId, 2);
        $detail  = "{$content['project']}/{$content['part_company']}/{$content['classify']} 数据ID:{$objectId}】";

        return self::addActionLog($event, $detail);
    }

    /**
     * 添加操作日志（普通）
     */
    public static function addActionLog($event, $detail = '')
    {
        $adminLog               = new ActionLog();
        $adminLog->user_id      = user_id();
        $adminLog->company_id   = company_id() ?? 0;
        $adminLog->user_name    = user() ? user()->nickname : '';
        $adminLog->company_name = company_name() ?: '';
        $adminLog->role_name    = role_name() ?: '';
        $adminLog->event        = $event;
        $adminLog->detail       = $detail;

        $result              = self::getRequestAddress();
        $adminLog->action_ip = $result['ip']; //记录ip
        $adminLog->address   = $result['address']; //保留省市

        $adminLog->save();

        if ($result['address']) self::addNewUserLoginInfo($result['address']);

        return $adminLog->id;
    }

    /**
     * 如果user_login_info里面没有用户新的登录地址，新增一条数据
     *
     * @param $address
     * @author: lxk
     * @date: 2024/6/15 9:00
     */
    public static function addNewUserLoginInfo($address)
    {
        $info = UserLoginInfo::query()->where('user_id', user_id())->where('login_address', $address)->first();
        if (empty($info)) {
            $apiKey  = config('site')['map_key'];
            $url     = 'https://restapi.amap.com/v3/geocode/geo?key=' . $apiKey . '&address=' . urlencode($address);
            $resJson = file_get_contents($url);
            $resArr  = json_decode($resJson, true);
            $lngLat  = $resArr['geocodes'][0]['location'];
            $lngLat  = explode(',', $lngLat);
            $lng     = $lngLat[0];
            $lat     = $lngLat[1];
            UserLoginInfo::query()->insert([
                'user_id'       => user_id(),
                'username'      => user() ? user()->nickname : '',
                'login_address' => $address,
                'login_lng'     => $lng,
                'login_lat'     => $lat,
                'login_time'    => date('Y-m-d H:i'),
            ]);
        }
    }

    /**
     * 获取当前请求用户的地址
     */
    public static function getRequestAddress($loginIp = '')
    {
        if (empty($loginIp)) {
            $loginIp = request()->ip();
        }
        $address = (new \Ip2Region())->simple($loginIp);
        $address = substr($address, 0, strpos($address, '【')); //去掉运营商【移动】
        $address = substr($address, strpos($address, '中国') + 6); //去掉国家

        return [
            'ip'      => $loginIp,
            'address' => $address ?: '本地操作',
        ];
    }

    /**
     * 获取页面当前位置 【科技档案】
     * 项目/参见单位
     */
    public static function getNowLocation($partCompanyId, $projectClassifyId = '')
    {
        $partCompany = PartCompany::query()->find($partCompanyId);
        $projectId   = $partCompany->project_id;
        $projectName = Project::query()->where('id', $projectId)->value('title');
        if ($projectClassifyId) {
            $projectClassify = ProjectClassify::query()->find($projectClassifyId);
            $nowLocation = $projectName . '/ ' . $partCompany->title . '/ ' . $projectClassify->title;
        } else {
            $nowLocation = $projectName . '/' . $partCompany->title;
        }

        return '当前位置：' . $nowLocation;
    }

    /**
     * 获取页面当前位置 【照片档案】
     * 项目/参见单位
     */
    public static function getPhotoNowLocation($partCompanyId, $photoClassifyId = '')
    {
        $partCompany = PartCompany::query()->find($partCompanyId);
        $projectId   = $partCompany->project_id;
        $projectName = Project::query()->where('id', $projectId)->value('title');

        if ($photoClassifyId) {
            $photoClassify = PhotoClassify::query()->find($photoClassifyId);
            $nowLocation   = $projectName . ' / ' . $partCompany->title . ' / ' . $photoClassify->title;
        } else {
            $nowLocation = $projectName . ' / ' . $partCompany->title;
        }

        return '当前位置：' . $nowLocation;
    }

    /**
     * 获取基础档案号
     * 例如 J•SWGS•5HQ279/328-1 中获取 J•SWGS•5HQ
     */
    public static function getBaseArchivesNo($partCompanyId, $projectClassifyId, $type = 1)
    {
        $projectId = PartCompany::query()->where('id', $partCompanyId)->value('project_id');
        if ($type == 2) { //照片档案
            $info = PhotoClassify::query()->find($projectClassifyId);
        } else { //纸质版单位工程
            $info = ProjectClassify::query()->find($projectClassifyId);
        }
        $baseArchivesNo = $info->archives_no;
        if (str_contains($baseArchivesNo, '[项目代码]')) {
            $documentNumber = Project::query()->where('id', $projectId)->value('document_number');
            $baseArchivesNo = str_replace('[项目代码]', $documentNumber, $baseArchivesNo);
        }
        return format_archives_no($baseArchivesNo);
    }

    /**
     * 是否是公司管理员
     */
    public static function isAdmin()
    {
        return CompanyUser::query()
            ->where('company_id', company_id())
            ->where('user_id', user_id())
            ->value('is_admin');
    }

    /**
     * 管理员等级
     */
    public static function adminLevel()
    {
        $roleId = CompanyUser::query()
            ->where('company_id', company_id())
            ->where('user_id', user_id())
            ->value('role_id');

        return Role::query()->where('id', $roleId)->value('admin_level');
    }

    /**
     * 判断文件夹是否存在，不存在则创建
     *
     * @param $dir
     * @return string
     * @author: lxk
     * @date: 2024-12-16 15:30
     */
    public static function createDir($dir) : string
    {
        return Storage::makeDirectory("public/{$dir}");
    }

    /**
     * 获取 项目名/参见单位名/项目分类名
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $bookTitleId
     * @return string
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/2 15:54
     */
    public function getBookContent($partCompanyId, $projectClassifyId, $bookTitleId): string
    {
        $info = (new PartCompany())->setTable('pt')->from('part_companies as pt')
            ->leftJoin('projects as p', 'p.id', '=', 'pt.project_id')
            ->where('pt.id', $partCompanyId)
            ->select('p.title as project', 'pt.title as part_company')
            ->first()
            ->toArray();

        if ($projectClassifyId && $bookTitleId) {
            $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
            $projectClassifyId = $bookTitleInstance->where('id', $bookTitleId)->value('project_classify_id');
            $projectClassify   = ProjectClassify::query()->find($projectClassifyId);
            $content           = "{$info['project']}/{$info['part_company']}/{$projectClassify->title}";
        } else {
            $content = "{$info['project']}/{$info['part_company']}}";
        }

        return $content;
    }

    /**
     * 生成 zip并且删除原来的文件夹
     * 待压缩的文件在 'D:/phpstudy_pro/WWW/GitPro/newRailway/public/storage/excels/南郑特大桥案卷序号【54~74】141156';
     * $dirStorage = 'storage/excels/南郑特大桥案卷序号【54~74】141156';
     */
    public static function createZip($dirStorage)
    {
        $files = File::allFiles($dirStorage);
        if (empty($files)) return '';
        $fileArr = [];
        foreach ($files as $file) {
            $tmp['real_path'] = $file->getRealPath();
            $tmp['file_name'] = $file->getFilename();
            $tmp['file_size'] = round(($file->getSize()) / 1024, 1) . 'KB'; //文件大小K,四舍五入保留一位小数
            $fileArr[]        = $tmp;
        }
        $zip = new \ZipArchive();
        $zipFile = $dirStorage . '.zip'; //定义一个压缩文件
        if ($zip->open($zipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) {
            $zipName = substr($dirStorage, strrpos($dirStorage, '/') + 1); //获取要压缩的文件夹名称，作为压缩文件名
            foreach ($fileArr as $fileInfo) {
                $fileName  = $fileInfo['file_name'];
                $localName = $zipName . '/' . $fileName;
                $zip->addFile($fileInfo['real_path'], $localName);
            }
            $zip->close();
            $needDelDir = str_replace('storage', '/public', $dirStorage);
            Storage::deleteDirectory($needDelDir); //生成zip之后，删除文件夹
            return config('site.domain') . $zipFile;
        } else {
            throw new ApiException('创建zip文件失败！');
        }
    }


}
